package org.linloong.utils;

import cn.hutool.core.annotation.Alias;
import cn.hutool.core.text.csv.CsvData;
import cn.hutool.core.text.csv.CsvReader;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import lombok.NoArgsConstructor;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@NoArgsConstructor
public final class HutoolExcelCsvUtil {

    /** 入口：同时支持 Excel / CSV */
    public static <T> List<T> readAndCheckHeader(MultipartFile file, Class<T> clazz) throws IOException {
        String fn = file.getOriginalFilename();
        try (InputStream in = file.getInputStream()) {
            if (fn != null && fn.endsWith(".csv")) {
                return readCsv(in, clazz);
            } else if (fn != null && (fn.endsWith(".xls") || fn.endsWith(".xlsx"))) {
                return readExcel(in, clazz);
            }
        }
        throw new IllegalArgumentException("仅支持 .xls/.xlsx/.csv");
    }

    /* ---------------- Excel 分支（Hutool-poi） ---------------- */
    private static <T> List<T> readExcel(InputStream in, Class<T> clazz) {
        // 1. 期望表头
        Set<String> expected = getExpectedHeaders(clazz);
        // 2. 真实表头
        ExcelReader reader = ExcelUtil.getReader(in);
        List<List<Object>> headRow = reader.read(0);   // 只读第一行
        Set<String> actual = headRow.get(0).stream()
                .map(Object::toString)
                .map(String::trim)
                .collect(Collectors.toSet());
        checkHeader(expected, actual);
        // 3. 读数据
        return reader.read(0, 1, clazz);
    }

    /* ---------------- CSV 分支（Hutool CsvReader） ---------------- */
    private static <T> List<T> readCsv(InputStream in, Class<T> clazz) throws IOException {
        // 1. 期望表头
        Set<String> expected = getExpectedHeaders(clazz);
        // 2. 真实表头
        CsvReader csvReader = CsvUtil.getReader();
        csvReader.setContainsHeader(true);   // 把第一行当表头
        CsvData data = csvReader.read(new InputStreamReader(in, StandardCharsets.UTF_8));
        Set<String> actual = new HashSet<>(data.getHeader());
        checkHeader(expected, actual);
        // 3. 读数据
        return data.getRows().stream()
                .skip(1)        // 跳过表头
                .map(row -> row.toBean(clazz))
                .collect(Collectors.toList());
    }

    /* ---------------- 拿 @Alias 值作为期望表头 ---------------- */
    private static Set<String> getExpectedHeaders(Class<?> clazz) {
        return Arrays.stream(clazz.getDeclaredFields())
                .filter(f -> f.isAnnotationPresent(Alias.class))
                .map(f -> f.getAnnotation(Alias.class).value())
                .collect(Collectors.toSet());
    }

    /* ---------------- 统一校验 ---------------- */
    private static void checkHeader(Set<String> expected, Set<String> actual) {
        if (actual.size() != expected.size()) {
            throw new IllegalArgumentException("表头数量不符：期望=" + expected.size() + "，实际=" + actual.size());
        }
        Set<String> missing = new HashSet<>(expected);
        missing.removeAll(actual);
        if (!missing.isEmpty()) {
            throw new IllegalArgumentException("表头缺失/名称不符：" + missing);
        }
    }
}
